//
//  AppDelegate.m
//  BackgroundTask
//
//  Created by Felix Yin on 2019/5/21.
//  Copyright © 2019 Felix Yin. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@property(nonatomic,assign) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@property(nonatomic,strong) NSTimer *myTimer;

@end

@implementation AppDelegate

/*
 
 当一个iOS应用被送到后台，它的主线程会被暂停。你用NSThread的detachNewThreadSelector:toTaget:withObject:类方法创建的线程也被挂起了。如果你想在后台完成一个长期任务，就必须调用UIApplication的beginBackgroundTaskWithExpirationHandler:实例方法，来向iOS借点时间。UIApplication的backgroundTimeRemaining属性包含了程序完成他的任务可以使用的秒数。如果在这个期限内，长期任务没有被完成，iOS将终止程序。每个对beginBackgroundTaskWithExpirationHandler:方法的调用，必须要相应的调用endBackgroundTask:方法（UIApplication的另一个实例方法）。也就是说，如果你向iOS要更多时间来完成一个任务，你必须告诉iOS你什么时候能完成那个任务，那时，你的程序将和其所有被暂停的线程被放入后台。
 和其所有被暂停的线程被放入后台。
 
 当你的程序在前台时，UIApplication的backgroundTimeRemaining属性等于DBL_MAX常量，这是double类型可表示的最大值（和这个值相当的integer通常等于-1）。在iOS被要求在程序被完全挂起之前给于更多的执行时间，这个属性指明了在完成任务前程序拥有多少秒。
 
 在程序中你可以多次调用beginBackgroundTaskWithExpirationHandler:方法。要记住的重点是，当iOS为你的程序返回一个token或者任务标识（task identifier）时，你都必须调用endBackgroundTask:方法，在运行的任务结束时，用来标志任务结束。如果你不这么做的话，iOS会终止你的程序。
 
 在后台时，程序不应该执行完全的功能，也不应该处理大量数据。事实上，他们只应该完成一个长期任务。
 
 比如，一个程序正在调用一个web service API，并且还没有从服务器上的那个API接收到响应。在此期间，如果程序被送入后台，它可以请求更多的时间，直到它从服务器收到响应。一旦响应被接收，程序必须保存其状态，并调用UIApplication的endBackgroundTask:实例方法将任务标记为完成。
 
 让我们看一个例子。我将从在应用程序委托中定义一个UIBackgroundTaskIdentifier类型的属性开始。同时，让我们定义一个NSTimer，当程序被送到后台时，我们将用它每隔1秒向控制台窗口输出一条消息：
 
 */

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

- (void) timerHandle:(NSTimer *) timer{
    //remaining 后台执行剩余时间
    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
    if (backgroundTimeRemaining == DBL_MAX) {
        NSLog(@"Background Time Remaining == Undetermined");
    }else{
        NSLog(@"Background Time Remaining == %.02f Seconds",backgroundTimeRemaining);
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    __weak typeof(self) weakSelf = self;
    if ([[UIDevice currentDevice] isMultitaskingSupported]) {
        self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerHandle:) userInfo:nil repeats:YES];
        // 当不添加 [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil] 时，程序进入后台后 不会执行定时器任务
        self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            //后台任务时间执行最大时间结束后 需要终止操作
            [weakSelf endBackgroundTask];
        }];
    }
}

//后台任务结束之后执行
- (void) endBackgroundTask{
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"结束进程");
        [weakSelf.myTimer invalidate];
        [[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTaskIdentifier];
        weakSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    });
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    if (self.backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
        [self endBackgroundTask];
    }
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}


@end
